package cn.autumnorange.app.user.consumer.authorization.security.security;

import cn.autumnorange.app.common.consumer.rpc.interceptor.filter.HttpServletRequestRPCWrapperRequestContextFilter;
import cn.autumnorange.app.common.rpc.exception.JsonException;
import cn.autumnorange.app.common.rpc.security.HttpSessionRequestCacheWrapper;
import cn.autumnorange.app.user.consumer.authorization.security.security.ajaxjsonlogin.AjaxJsonUsernamePasswordAuthenticationFilter;
import cn.autumnorange.app.user.consumer.authorization.security.security.basicLogin.JsonBasicAuthenticationEntryPoint;
import cn.autumnorange.app.user.consumer.authorization.security.security.basicLogin.JsonBasicAuthenticationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2SsoProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.Arrays;
import java.util.List;

// node should everyThing loginType create Filter
// 由于spring oauth2自带有spring security。因此默认情况下，对所有的接口都添加认证。我们需要请求连接进行授权或不授权处理，个性化登录等等。源码如下
@Configuration
public class MerryyouSecurityConfig extends WebSecurityConfigurerAdapter {

  @Value("${loginPage}")
  private String loginPage;

  private String realm = "oauth2AuthorizationServer";

  @Value("#{'${urlFilterList}'.split(',')}")
  private List<String> urlFilterList;

  @Autowired private MerryyounExpiredSessionStrategy merryyounExpiredSessionStrategy;

  @Autowired
  private HttpServletRequestRPCWrapperRequestContextFilter
      httpServletRequestRPCWrapperRequestContextFilter;
  //    通用错误处理
  //    注意:执行顺序按照从上到下 这里如果 @Autowired JsonException放在@Bean JsonBasicAuthenticationEntryPoint之后
  //    则@Bean先执行 @Bean有注入该类实例则会为null
  @Autowired private JsonException jsonExceptionImpl;

  @Bean
  @Override
  public AuthenticationManager authenticationManagerBean() throws Exception {
    AuthenticationManager manager = super.authenticationManagerBean();
    return manager;
  }

  // oauth2模式接口异常处理类以及登录类型过滤器filter异常处理类
  @Bean
  public AuthenticationEntryPoint jsonBasicAuthenticationEntryPoint() {
    return new JsonBasicAuthenticationEntryPoint(realm, jsonExceptionImpl);
  }
  // 异常处理
  @Bean
  public AccessDeniedHandler jsonAccessDeniedHandlerImpl() {
    return new JsonAccessDeniedHandlerImpl(jsonExceptionImpl);
  }

  @Bean
  public AuthenticationFailureHandler jsonSimpleUrlAuthenticationFailureHandler() {
    //        loginPage + "?error"
    return new JsonSimpleUrlAuthenticationFailureHandler(jsonExceptionImpl);
  }

  // oauth2模式接口异常处理类以及登录类型过滤器filter异常处理类
  @Autowired private AuthenticationEntryPoint jsonBasicAuthenticationEntryPoint;
  // 详细见oauth2.md
  @Autowired private AccessDeniedHandler jsonAccessDeniedHandlerImpl;

  //    oauth2 json类型登录解析类
  @Autowired private LoginTypeService ajaxJsonLoginTypeServiceImpl;

  //   oauth2 form类型登录解析类
  @Autowired private LoginTypeService formLoginTypeServiceImpl;

  //    //    form类型非oauth2模式校验登录成功处理类

  //    form类型非oauth2模式校验登录失败处理类
  @Autowired private AuthenticationFailureHandler jsonSimpleUrlAuthenticationFailureHandler;

  //  直接单独第一次就请求auth/login时
  // 配置ajaxJsonUsernamePasswordAuthenticationFilter登录成功后跳转到网关login进而拿到客户端session并得到token
  @Value("${redirectUris}")
  private String redirectUris;

  /** 自定义登录过滤器 */
  private AbstractAuthenticationProcessingFilter AjaxJsonUsernamePasswordAuthenticationFilter()
      throws Exception {
    AjaxJsonUsernamePasswordAuthenticationFilter ajaxJsonUsernamePasswordAuthenticationFilter =
        new AjaxJsonUsernamePasswordAuthenticationFilter(
            OAuth2SsoProperties.DEFAULT_LOGIN_PATH, ajaxJsonLoginTypeServiceImpl);

    // SimpleUrlAuthenticationSuccessHandler successHandler = new
    // SimpleUrlAuthenticationSuccessHandler();
    // successHandler.setAlwaysUseDefaultTargetUrl(true);
    // successHandler.setDefaultTargetUrl("/user");

    ajaxJsonUsernamePasswordAuthenticationFilter.setAuthenticationManager(
        this.authenticationManager());
    SavedRequestAwareAuthenticationSuccessHandler savedRequestAwareAuthenticationSuccessHandler =
        new SavedRequestAwareAuthenticationSuccessHandler();
    savedRequestAwareAuthenticationSuccessHandler.setDefaultTargetUrl(redirectUris);
    ajaxJsonUsernamePasswordAuthenticationFilter.setAuthenticationSuccessHandler(
        savedRequestAwareAuthenticationSuccessHandler);
    ajaxJsonUsernamePasswordAuthenticationFilter.setAuthenticationFailureHandler(
        jsonSimpleUrlAuthenticationFailureHandler);

    return ajaxJsonUsernamePasswordAuthenticationFilter;
  }

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    //                所有的接口都需要权限认证 配置了配置WebSecurityConfigurerAdapter的话会重定向跳转到登录页面
    // 禁用缓存
    http.headers()
        .cacheControl()
        .and()
        .frameOptions()
        .disable()
        .and()
        .formLogin()
        // 定义loginPage url为login_p抛出异常最终都会走login_p服务 "/login_p"
        //            全部在自定义    jwtTokenAuthenticationFilter 替换
        // UsernamePasswordAuthenticationFilter设置
        .loginPage(loginPage)
        .loginProcessingUrl(OAuth2SsoProperties.DEFAULT_LOGIN_PATH)
        //                .usernameParameter(LoginTypeService.USER_NAME)
        //                .passwordParameter(LoginTypeService.PASSWORD)
        //                form登录自定义成功 失败处理器
        //                .failureHandler(jsonSimpleUrlAuthenticationFailureHandler)
        //                .successHandler(authenticationSuccessHandlerImpl)
        //                .and()
        //// 为set ExceptionTranslationFilter为倒数第二的filter 异常处理端点,其目前知道是处理授权码接口抛出的异常
        ////                 .accessDeniedHandler(jsonAccessDeniedHandlerImpl)
        //
        // .exceptionHandling().authenticationEntryPoint(jsonBasicAuthenticationEntryPoint)
        //                .accessDeniedHandler(jsonAccessDeniedHandlerImpl)
        // 禁用basic账号密码登录
        .and()
        .httpBasic()
        .disable()

        //        .and()
        //            启用cors跨域支持
        .cors()
        //            //            禁用cors跨域支持
        .disable()
        //            .and()
        // HttpOnlyFalse 允许浏览器脚本访问cookie
        .csrf()
        .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
        // 禁用跨站伪造
        .disable()
        .logout()
        .deleteCookies("SESSIONID");

    // 并发登录   https://www.jianshu.com/p/b9332491f500
    http.sessionManagement()
        //        .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .maximumSessions(1) // 最大session并发数量1
        .maxSessionsPreventsLogin(false) // false之后登录踢掉之前登录,true则不允许之后登录
        .expiredSessionStrategy(merryyounExpiredSessionStrategy); // 登录被踢掉时的自定义操作

    //        http.addFilterAt(new AjaxJsonLoginAuthenticationFilter(this.authenticationManager(),
    // jsonBasicAuthenticationEntryPoint, ajaxJsonLoginTypeServiceImpl),
    // BasicAuthenticationFilter.class);
    //        http.addFilterBefore(new FormLoginAuthenticationFilter(this.authenticationManager(),
    // jsonBasicAuthenticationEntryPoint, formLoginTypeServiceImpl),
    // AjaxJsonLoginAuthenticationFilter.class);
    // WebAsyncManagerIntegrationFilter
    //        配置接口调用headerFilter到过滤链中
    http.addFilterBefore(
        httpServletRequestRPCWrapperRequestContextFilter, WebAsyncManagerIntegrationFilter.class);
//    http.addFilterAt(
//        new JsonBasicAuthenticationFilter(
//            this.authenticationManager(), jsonBasicAuthenticationEntryPoint),
//        BasicAuthenticationFilter.class);
    http.addFilterAt(
        AjaxJsonUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    http.setSharedObject(RequestCache.class, new HttpSessionRequestCacheWrapper());
    //修正处理客户端验证 DaoAuthenticationProvider UserDetailsService 注入错误 需要的类型ClientDetailsUserDetailsService
//    ProviderManager authenticationManager = (ProviderManager) this.authenticationManager();
//    List<AuthenticationProvider> providers = authenticationManager.getProviders();
//    if (providers != null && providers.size() > 0) {
//      for (AuthenticationProvider provider : providers) {
//        if (provider instanceof DaoAuthenticationProvider) {
//          ((DaoAuthenticationProvider) provider).setUserDetailsService(new ClientDetailsUserDetailsService(clientDetailsService));
//        }
//      }
//    }
  }
  //ClientDetailsService是注入的方式
  @Autowired
  private ClientDetailsService clientDetailsService;
  @Override
  public void configure(WebSecurity web) throws Exception {
    //        web.ignoring()
    //                .antMatchers("/test1");
    //                .antMatchers("/index.html", "/static/**", "/sysUser/**")
    //                .antMatchers("/webjars/**")
    //                .antMatchers("/v2/**")
    //                .antMatchers("/swagger-resources/**")
    //                .antMatchers(
    //                        SecurityConstants.DEFAULT_SOCIAL_PROCESS_URL,
    //                        SecurityConstants.FAVICON_ICO,
    //                        SecurityConstants.STATICRESOURCES,
    //                        "/**/*.js",
    //                        "/**/*.css",
    //                        "/**/*.jpg",
    //                        "/**/*.jpeg",
    //                        "/**/*.png",
    //                        "/**/*.woff2",
    //                        "/code/*");
    // 解决静态资源被拦截的问题
    String[] urlFilterArray = new String[urlFilterList.size()];
    web.ignoring().antMatchers(urlFilterList.toArray(urlFilterArray));
  }

  //    @Bean
  //    public CorsConfigurationSource corsConfigurationSource() {
  //        CorsConfiguration configuration = new CorsConfiguration();
  //
  // configuration.setAllowedOrigins(Arrays.asList("http://localhost:8082","http://localhost:3000","http://localhost:3002","http://172.16.90.177:8888"));
  //        configuration.setAllowedMethods(Arrays.asList("*"));
  //        configuration.addAllowedHeader("*");
  //        configuration.setAllowCredentials(true);
  //
  //        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
  //        source.registerCorsConfiguration("/**", configuration);
  //        return source;
  //    }
}
